package com.gitee.fanx.rpc.client;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.*;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.*;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.NonNull;
import org.springframework.util.*;

import java.util.*;

/**
 * @author fanhang
 */
@AutoConfigureAfter(RpcClientProxyFactory.class)
public class RpcClientsRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(@NonNull AnnotationMetadata metadata, @NonNull BeanDefinitionRegistry registry) {
		String startPackage = this.getStartPackage(metadata);
		// 扫描 RpcClient
		RpcClientBeanDefinitionScanner scanner = new RpcClientBeanDefinitionScanner(registry);
		Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(startPackage);
		// 遍历 RpcClient 结果
		for (BeanDefinition candidateComponent : beanDefinitions) {
			if (candidateComponent instanceof AnnotatedBeanDefinition beanDefinition) {
				AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
				Assert.isTrue(annotationMetadata.isInterface(), "@RpcClient can only be specified on an interface");
				AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDefinition);
				String className = beanDefinition.getBeanClassName();
				Class<?> rpcClazz = ClassUtils.resolveClassName(className, null);
				this.registerRpcClient(registry, rpcClazz);
			}
		}
	}

	private void registerRpcClient(BeanDefinitionRegistry registry, Class<?> rpcClazz) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RpcClientProxyFactoryBean.class);
		GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
		definition.setBeanClass(RpcClientProxyFactoryBean.class);
		ConstructorArgumentValues argumentValues = definition.getConstructorArgumentValues();
		argumentValues.addGenericArgumentValue(rpcClazz);
		definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
		registry.registerBeanDefinition(rpcClazz.getSimpleName(), definition);

	}

	private String getStartPackage(AnnotationMetadata metadata) {
		String startClazz = metadata.getClassName();
		String startPackage = ClassUtils.getPackageName(startClazz);
		Assert.hasText(startPackage, "required startPackage");
		return startPackage;
	}

	/**
	 * 自定义扫描 RpcClient
	 */
	public static class RpcClientBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
		public RpcClientBeanDefinitionScanner(BeanDefinitionRegistry registry) {
			super(registry);
			super.addIncludeFilter(new AnnotationTypeFilter(RpcClient.class));
		}
		@Override
		protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
			return beanDefinition.getMetadata().isInterface();
		}
	}
}
